Ruby 日記 30日目: ブロック引数とProcオブジェクト
実行してもエラーにならないコードを選べ
選択肢:
code:gold/ex30/choice01.rb
def bar(&block)
block.yield
end
bar do
puts "hello, world"
end
code:gold/ex30/choice02.rb
def bar(&block)
block.call
end
bar do
puts "hello, world"
end
code:gold/ex30/choice03.rb
def bar(&block, n)
block.call
end
bar(5) do
puts "hello, world"
end
code:gold/ex30/choice04.rb
def bar(n, &block)
block.call
end
bar(5) do
puts "hello, world"
end
解説:
まずはRubyのメソッド定義について
文法:
code:rb
式.. (body)
式..]..
[else
式..]
[ensure
式..]
end
メソッド定義において、仮引数はその種類毎に以下の順序でしか指定すること はできません。いずれも省略することは可能です。
デフォルト式のない引数(複数指定可)
デフォルト式のある引数(複数指定可)
* を伴う引数(1つだけ指定可)
デフォルト式のない引数(複数指定可)
キーワード引数(複数指定可)
** を伴う引数(1つだけ指定可)
& を伴う引数(1つだけ指定可)
なので、def bar(&block, n) と書いてるchoice03.rbはエラーになる。
続いてブロック引数について
その実体はProcクラスのオブジェクトなのだね〜
code:rb
def bar(&block)
p block.class # => Proc
block.yield
end
bar do
puts "hello, world"
end
ではProcクラスのyieldメソッドやcallメソッドを調べてみよう
call(*arg) -> ()
yield(*arg) -> ()
手続きオブジェクトを実行してその結果を返します。
なんとyieldメソッドはcallメソッドと同じだった(callメソッドは知ってたけどyieldメソッドは知らなかったよ)
うわ〜〜〜これ忘れていた〜〜〜。同じなのか〜〜〜〜。
callメソッドを使っている choice02, choice04 を選んじゃったよ〜
「yieldメソッドはcallメソッドと同じ」 絶対忘れないぞ
じゃあchoice01もchoice02もエラーにならずに動くね〜
choice04は未使用の引数nを渡しているだけで、これ自体は特に問題ないね
なので正解は choice01, choice02, choice04 だね
code:sh
# ruby gold/ex30/choice01.rb
hello, world
# ruby gold/ex30/choice02.rb
hello, world
# ruby gold/ex30/choice03.rb
gold/ex30/choice03.rb:1: syntax error, unexpected ',', expecting ')'
def bar(&block, n)
^
# ruby gold/ex30/choice04.rb
hello, world
/icons/hr.icon
yieldについて
自分で定義したブロック付きメソッドでブロックを呼び出すときに使います。 yield に渡された値はブロック記法において | と | の間にはさまれた 変数(ブロックパラメータ)に代入されます。
引数をブロックパラメータとして渡してブロックを評価します。yield は イテレータを定義するために クラス/メソッドの定義/メソッド定義 内で使用します。
code:rb
# ブロック付きメソッドの定義、
# その働きは与えられたブロック(手続き)に引数1, 2を渡して実行すること
def foo
yield(1,2)
end
# fooに「2引数手続き、その働きは引数を配列に括ってpで印字する」というものを渡して実行させる
foo {|a,b|
# 今度は「2引数手続き、その働きは足し算をしてpで印字する」というものを渡して実行させる
foo {|a, b|
p a + b
} # => 3 (要するに p 1 + 2 を実行した)
わかりやすい例だね
code:rb
# 今度のブロック付きメソッドの働きは、
# 与えられたブロックに引数10を渡して起動し、続けざまに引数20を渡して起動し、
# さらに引数30を渡して起動すること
def bar
yield 10
yield 20
yield 30
end
# barに「1引数手続き、その働きは引数に3を足してpで印字する」というものを渡して実行させる
bar {|v|
p v + 3
}
# => 13
# 23
# 33 (同じブロックが3つのyieldで3回起動された。
# 具体的には p 10 + 3; p 20 + 3; p 30 + 3 を実行した)
これもわかりやすい
動きはわかるんだけど、yield自体がどこで定義されているのかよく分かっていない〜
Proc#yieldとは違うわけじゃん?
code:rb
def foo
pr = Proc.new
pr.call(1)
end
foo {|arg| p arg }
# => 1
これは以下と同じです。
code:rb
def foo
yield(1)
end
foo {|arg| p arg }
# => 1
ふ〜んって感じ
今回の問題でいえばこういう風にも書けるよってことだね
code:gold/ex30/yield.rb
def bar(&block)
yield # これは block.call と同じ
end
bar do
puts "hello, world"
end
code:sh
# ruby gold/ex30/yield.rb
hello, world